//888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
//	File		 :	sequence.cxx
//	Contents	 :	Source file for private functions manipulating CSequence
//	Creation Date:	22/12/1999
//	Last Modified:	23/12/1999	lg
//	tabs width	 :	every 4 characters
//
//888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

namespace
{
inline const char isvalid(const char* LookupTable, char c) {return LookupTable[c];}
void ConstructLookupTable(char* p, bool allowLower, const std::string& invalid)
{
#ifndef NDEBUG
	//make sure all invalid letters are upper case
	for (int i = invalid.length() - 1; i >= 0; --i)
		assert(invalid[i] >= 'A' && invalid[i] <= 'Z');
#endif

	// start with blank sheet
	memset(p, 0, 256);

	// turn on all caps
	for (char i = 'A'; i <= 'Z'; ++i)
		p[i] = (char)1;

	// turn off invalid letters
	for (int i = invalid.length() - 1; i >= 0; --i)
		p[invalid[i]] = 0;


	if (allowLower)
	{
		// turn on all lower case letters
		for (char i = 'a'; i <= 'z'; ++i)
			p[i] = (char)1;

		// turn off invalid letters
		for (int i = invalid.length() - 1; i >= 0; --i)
			p[invalid[i] + 'a' -'A'] = 0;
	}
}

//_______________________________________________________________________________________
//
//	Left/Right/Center JustifyNames
//
//		justify sequence nameswith whitespace
//		Last modified: lg 23/12/1999
//
//_______________________________________________________________________________________

void RightJustifyNames(CSequence& seq)
{
	unsigned len = seq.GetNamesMaxLen();
	for (int i = seq.Count() - 1; i >= 0; --i)
		seq.Names[i].insert(std::string::size_type(0),
									len - seq.Names[i].length(), ' ');
	for (int i = seq.ExtraCount() - 1; i >= 0; --i)
		seq.ExtraNames[i].insert(std::string::size_type(0),
									len - seq.ExtraNames[i].length(), ' ');
}

void LeftJustifyNames(CSequence& seq)
{
	unsigned len = seq.GetNamesMaxLen();
	for (int i = seq.Count() - 1; i >= 0; --i)
		seq.Names[i].append(len - seq.Names[i].length(), ' ');
	for (int i = seq.ExtraCount() - 1; i >= 0; --i)
		seq.ExtraNames[i].append(len - seq.ExtraNames[i].length(), ' ');
}

void CentreJustifyNames(CSequence& seq)
{
	unsigned len = seq.GetNamesMaxLen();
	for (int i = seq.Count() - 1; i >= 0; --i)
	{
		unsigned half = (len - seq.Names[i].length()) >> 1;
		seq.Names[i].insert(std::string::size_type(0),
								len - half - seq.Names[i].length(), ' ');
		seq.Names[i].append(half, ' ');
	}

	for (int i = seq.ExtraCount() - 1; i >= 0; --i)
	{
		unsigned half = (len - seq.ExtraNames[i].length()) >> 1;
		seq.ExtraNames[i].insert(std::string::size_type(0),
									len - half - seq.ExtraNames[i].length(), ' ');
		seq.ExtraNames[i].append(half, ' ');
	}
}

// anonymous namespace for functions specific to this translational unit

//_______________________________________________________________________________________
//
//	CountDigits
//
//		returns the number of digits in a string i.e. Log10
//		Last modified: lg 23/12/1999
//
//_______________________________________________________________________________________
inline unsigned CountDigits(unsigned i)
{
    if (i < 10)
        return 1;
    else if (i < 100)
        return 2;

    assert(i < 1000);
    return 3;
}


//_______________________________________________________________________________________
//
//	ElideSequence
//
//		removes a section in the supplied sequence
//			from begin until end (exclusive aka STL)
//		cntResidues is an array containing the number of residues to be elided
//			i.e. not counting blanks
//		The logic is complicated because the number representing the number of elided
//			residues might be short enough to reveal the last 1-2 residue positions
//			in which case, the number of elided residues has to be reduced
//			e.g.
//					AAA(100)AAAA
//				    AAA(55)BAAAA    where B was supposed to be elided but because
//									55 is shorter than 100, it is revealed
//		Last modified: lg 23/12/1999
//
//_______________________________________________________________________________________
// lT == lookupTable
// PH == placeholder
void ElideSequence(CSequence& seq, unsigned begin, unsigned end, unsigned* cntResidues, char* lT)
{
    assert(begin < end);
    assert(end <= seq.Len);

    // remember to add two for two brackets ()
	// maximum placeholder length
    unsigned max_PH_Len = CountDigits(*std::max_element(cntResidues, cntResidues + seq.Count()))+2;

    // surely do not elide more than 999 residues
    assert(max_PH_Len < 6);
	if (max_PH_Len >= 6)
		return;

    char itoaBuf[10];
    char buf[6];

    buf[max_PH_Len] = 0;


    unsigned start_PH = end - max_PH_Len;
    for (unsigned i = 0; i < seq.Count(); ++i)
    {
        unsigned elidedResidues = cntResidues[i];
		// the buffer starts off with the end of the sparse region
		assert(max_PH_Len <= 6);
		assert(seq.Data[i].begin() + start_PH + max_PH_Len <= seq.Data[i].end());
		std::copy(seq.Data[i].begin() + start_PH,
					seq.Data[i].begin() + start_PH + max_PH_Len,
					buf);

        if (elidedResidues)
        {
			// do not elide if unnecessary
			// just copy all residues into the buffer
			if (elidedResidues == max_PH_Len)
			{
				CSequence::T_data::iterator j = seq.Data[i].begin() + begin;
				CSequence::T_data::iterator jEnd = seq.Data[i].begin() + end;
				char* pbuf = buf;
				while (j != jEnd)
				{
					if (isvalid(lT, *j))
						*pbuf++ = *j;
					++j;
				}
				assert(pbuf - buf == (int)max_PH_Len);

				// slot new section into place
		        seq.Data[i].replace(seq.Data[i].begin() + begin,
									seq.Data[i].begin() + end, buf, max_PH_Len);
				continue;
			}

			// see if we can get away with just remove a central stretch of gaps in
			// the middle
			else if (elidedResidues < max_PH_Len)
			{
				// copy all the continguous residues from the beginning and end of
				//  the sparse region into the buffer. If we have all the residues
				//  then we do not need a placeholder

				// copy the end of the sparse region into place
				std::copy(seq.Data[i].begin() + start_PH,
							seq.Data[i].begin() + start_PH + max_PH_Len,
							buf);

				// copy any continguous stretch of residues at the beginning of the
				//   sparse region
				CSequence::T_data::iterator j = seq.Data[i].begin() + begin;
				char* pbuf = buf;
				while (isvalid(lT, *j))
					*pbuf++ = *j++;
				assert(pbuf <= buf + elidedResidues);
				assert(j < seq.Data[i].begin() + end);


				// have we got all the residues?
				unsigned validCnt = 0;
				for (unsigned j = 0; j < max_PH_Len; ++j)
					if (isvalid(lT, buf[j]))
						++validCnt;

				// yes : no need for a place holder
				if (validCnt == elidedResidues)
				{
					// slot new section into place
			        seq.Data[i].replace(seq.Data[i].begin() + begin,
										seq.Data[i].begin() + end, buf, max_PH_Len);
					continue;
				}

				// restore the buffer so it starts off with the end of the sparse region
				std::copy(seq.Data[i].begin() + start_PH,
							seq.Data[i].begin() + start_PH + max_PH_Len,
							buf);
			}

			// create placeholder
			unsigned PH_Len = CountDigits(elidedResidues);
            for (unsigned j = PH_Len + 2; j <= max_PH_Len; ++j)
                if (isvalid(lT, buf[j]))
                    --elidedResidues;

			// check if more residues are uncovered by a short placeholder
			// if so, adjust placeholder value
			if (PH_Len != CountDigits(elidedResidues))
            {
                assert(PH_Len == CountDigits(elidedResidues) + 1);
                if (isvalid(lT, buf[PH_Len + 2 - 1]))
                    --elidedResidues;
                --PH_Len;
            }
            if (elidedResidues)
            {
                buf[0] = '(';
                buf[PH_Len+1] = ')';
				sprintf(itoaBuf, "%d", elidedResidues);
//                itoa(elidedResidues, itoaBuf, 10);
                for (unsigned j = 0; j < PH_Len; ++j)
                    buf[j+1] = itoaBuf[j];
            }
        }
        seq.Data[i].replace(seq.Data[i].begin() + begin, seq.Data[i].begin() + end, buf, max_PH_Len);
    }
    for (unsigned i = 0; i < seq.ExtraCount(); ++i)
        seq.ExtraData[i].erase(seq.ExtraData[i].begin() + begin + max_PH_Len, seq.ExtraData[i].begin() + end);
}


} // end anonymous namespace



char C_NOTWHITESPACE[256] =
{
    0,			  /* 00 (NUL) */
    0,		      /* 01 (SOH) */
    0,		      /* 02 (STX) */
    0,		      /* 03 (ETX) */
    0,		      /* 04 (EOT) */
    0,		      /* 05 (ENQ) */
    0,		      /* 06 (ACK) */
    0,		      /* 07 (BEL) */
    0,		      /* 08 (BS)  */
    0,		      /* 09 (HT)  */
    0,		      /* 0A (LF)  */
    0,		      /* 0B (VT)  */
    0,		      /* 0C (FF)  */
    0,		      /* 0D (CR)  */
    0,		      /* 0E (SI)  */
    0,		      /* 0F (SO)  */
    0,		      /* 10 (DLE) */
    0,		      /* 11 (DC1) */
    0,		      /* 12 (DC2) */
    0,		      /* 13 (DC3) */
    0,		      /* 14 (DC4) */
    0,		      /* 15 (NAK) */
    0,		      /* 16 (SYN) */
    0,		      /* 17 (ETB) */
    0,		      /* 18 (CAN) */
    0,		      /* 19 (EM)  */
    0,		      /* 1A (SUB) */
    0,		      /* 1B (ESC) */
    0,		      /* 1C (FS)  */
    0,		      /* 1D (GS)  */
    0,		      /* 1E (RS)  */
    0,		      /* 1F (US)  */
    -1,            /* 20 SPACE */
    -1,            /* 21 !     */
    -1,            /* 22 "     */
    -1,            /* 23 #     */
    -1,            /* 24 $     */
    -1,            /* 25 %     */
    -1,            /* 26 &     */
    -1,            /* 27 '     */
    -1,            /* 28 (     */
    -1,            /* 29 )     */
    -1,            /* 2A *     */
    -1,            /* 2B +     */
    -1,            /* 2C ,     */
    -1,            /* 2D -     */
    -1,            /* 2E .     */
    -1,            /* 2F /     */
    -1,            /* 30 0     */
    -1,            /* 31 1     */
    -1,            /* 32 2     */
    -1,            /* 33 3     */
    -1,            /* 34 4     */
    -1,            /* 35 5     */
    -1,            /* 36 6     */
    -1,            /* 37 7     */
    -1,            /* 38 8     */
    -1,            /* 39 9     */
    -1,            /* 3A :     */
    -1,            /* 3B ;     */
    -1,            /* 3C <     */
    -1,            /* 3D =     */
    -1,            /* 3E >     */
    -1,            /* 3F ?     */
    -1,            /* 40 @     */
    -1,            /* 41 A     */
    -1,            /* 42 B     */
    -1,            /* 43 C     */
    -1,            /* 44 D     */
    -1,            /* 45 E     */
    -1,            /* 46 F     */
    -1,            /* 47 G     */
    -1,            /* 48 H     */
    -1,            /* 49 I     */
    -1,            /* 4A J     */
    -1,            /* 4B K     */
    -1,            /* 4C L     */
    -1,            /* 4D M     */
    -1,            /* 4E N     */
    -1,            /* 4F O     */
    -1,            /* 50 P     */
    -1,            /* 51 Q     */
    -1,            /* 52 R     */
    -1,            /* 53 S     */
    -1,            /* 54 T     */
    -1,            /* 55 U     */
    -1,            /* 56 V     */
    -1,            /* 57 W     */
    -1,            /* 58 X     */
    -1,            /* 59 Y     */
    -1,            /* 5A Z     */
    -1,            /* 5B [     */
    -1,            /* 5C \     */
    -1,            /* 5D ]     */
    -1,            /* 5E ^     */
    -1,            /* 5F _     */
    -1,            /* 60 `     */
    -1,            /* 61 a     */
    -1,            /* 62 b     */
    -1,            /* 63 c     */
    -1,            /* 64 d     */
    -1,            /* 65 e     */
    -1,            /* 66 f     */
    -1,            /* 67 g     */
    -1,            /* 68 h     */
    -1,            /* 69 i     */
    -1,            /* 6A j     */
    -1,            /* 6B k     */
    -1,            /* 6C l     */
    -1,            /* 6D m     */
    -1,            /* 6E n     */
    -1,            /* 6F o     */
    -1,            /* 70 p     */
    -1,            /* 71 q     */
    -1,            /* 72 r     */
    -1,            /* 73 s     */
    -1,            /* 74 t     */
    -1,            /* 75 u     */
    -1,            /* 76 v     */
    -1,            /* 77 w     */
    -1,            /* 78 x     */
    -1,            /* 79 y     */
    -1,            /* 7A z     */
    -1,            /* 7B {     */
    -1,            /* 7C |     */
    -1,            /* 7D }     */
    -1,            /* 7E ~     */
    /* and the rest are 0... */
};
